/*
 * Copyright  2011 Nokia Corporation.
 *
 */

#include <QtGui>
#include <GLES2/gl2.h>


#ifdef Q_OS_LINUX
#include <QX11Info>
#endif

#ifdef Q_OS_SYMBIAN
//#include <mw/coecntrl.h>

#include <centralrepository.h>

// Uid for Profile repository
const TUid KCRUidProfileEngine =
{
    0x101F8798
};

// Key for warning and game tones enabled in active profile
const TUint32 KProEngActiveWarningTones = 0x7E000020;

#endif

#include "GameWindow.h"

using namespace GE;

GameWindow::GameWindow(QWidget *parent) :
    QWidget(parent)
{
    srand( getTickCount() );
#ifdef Q_OS_SYMBIAN
    iProfileRepository = CRepository::NewL(KCRUidProfileEngine);
#endif
    timerid = 0;
    setAutoFillBackground( false );
    setAttribute (Qt::WA_OpaquePaintEvent);
    setAttribute( Qt::WA_NoSystemBackground );
    setAttribute( Qt::WA_NativeWindow );
    setAttribute( Qt::WA_PaintOnScreen, true );
    setAttribute( Qt::WA_StyledBackground, false);
    setAttribute( Qt::WA_PaintUnclipped );
    m_paused = true;
    m_audioOutput = 0;
    m_audioEnabled = false;

    m_currentTime = 0;
    m_prevTime = m_currentTime;
    m_fps = 0.0f;
    m_frameTime = 0.0f;
}


unsigned int GameWindow::getTickCount()
{
    QTime now = QTime::currentTime();
    return (now.hour() * 3600 + now.minute() * 60 + now.second()) * 1000
            + now.msec();
}


GameWindow::~GameWindow()
{
#ifdef Q_OS_SYMBIAN
    delete iProfileRepository;
#endif
    stopAudio();
    destroy();
}


bool GameWindow::isProfileSilent()
{

#ifdef Q_OS_SYMBIAN

    TInt warningTonesEnabled = 1;
    if (iProfileRepository->Get(KProEngActiveWarningTones, warningTonesEnabled) == 0 && warningTonesEnabled == 0)
        return true;

#endif


    return false;
}


bool GameWindow::eventFilter(QObject *object, QEvent *event) {
    // http://doc.trolltech.com/4.7/qevent.html#QEvent
    if (event->type() == QEvent::ActivationChange) {
        if (m_paused)
            m_paused = false;
        else
            m_paused = true;

        if (m_paused)
            pause();
        else
            resume();

        qDebug() << "ActivationChangeFiltered() -> toggle pause";
        return false;
    }
    else {
        // Standard event processing
        return QObject::eventFilter(object, event);
    }
}


void GameWindow::create()
{
    setAttribute(Qt::WA_NoSystemBackground);
    createEGL();
    qDebug() << "Calling onCreate..";
    onCreate();


    m_currentTime = getTickCount();
    m_prevTime = m_currentTime;
    m_fps = 0.0f;
    m_frameTime = 0.0f;

    qDebug() << "Calling resume";
    resume();
    qDebug() << "::create finished()";
    startAudio();
}


void GameWindow::startAudio()
{
    if (isProfileSilent() == true) return;
    // Allready enabled
    if (m_audioOutput != 0)
        return;
    //m_audioMixer.setEnabled( true );
    m_audioEnabled = true;
    qDebug() << "Starting audio..";
    m_audioOutput = new GE::AudioOut( this, &m_audioMixer );

}


void GameWindow::stopAudio() {
    // Already stopped.
    if (m_audioOutput == 0)

        return;
    //m_audioMixer.setEnabled( false );
    m_audioEnabled = false;
    delete m_audioOutput;
    m_audioOutput = 0;
}


int GameWindow::onCreate()
{
    qDebug() << "int GE::GameWindow::onCreate()";
    return 1;
}


void GameWindow::createEGL()
{
    // EGL variables
    eglDisplay	= 0;
    eglConfig	= 0;
    eglSurface	= 0;
    eglContext	= 0;

#ifdef Q_OS_LINUX
    eglDisplay = eglGetDisplay(
                (EGLNativeDisplayType)this->x11Info().display());
#endif
#ifdef Q_OS_WIN32
    HWND hwnd = this->winId();
    HDC dc = GetWindowDC( hwnd );
    eglDisplay = eglGetDisplay((EGLNativeDisplayType)dc);
#endif
#ifdef Q_OS_SYMBIAN
    eglDisplay = eglGetDisplay (EGL_DEFAULT_DISPLAY);
#endif

    qDebug() << "theGameWindow->eglGetDisplay()" << eglDisplay;

    EGLint iMajorVersion, iMinorVersion;
    if (!eglInitialize(eglDisplay, &iMajorVersion, &iMinorVersion)) {
        qDebug() << "Error: eglInitialize() failed.\n";
        CleanupAndExit (eglDisplay);
    }

    qDebug() << "theGameWindow->eglInitialize()";


    EGLint pi32ConfigAttribs[13];
    pi32ConfigAttribs[0] = EGL_SURFACE_TYPE;
    pi32ConfigAttribs[1] = EGL_WINDOW_BIT;
    pi32ConfigAttribs[2] = EGL_RENDERABLE_TYPE;
    pi32ConfigAttribs[3] = EGL_OPENGL_ES2_BIT;
    /*
    pi32ConfigAttribs[4] = EGL_DEPTH_SIZE;
    pi32ConfigAttribs[5] = 16;
    pi32ConfigAttribs[6] = EGL_RED_SIZE;
    pi32ConfigAttribs[7] = 8;
    pi32ConfigAttribs[8] = EGL_GREEN_SIZE;
    pi32ConfigAttribs[9] = 8;
    pi32ConfigAttribs[10] = EGL_BLUE_SIZE;
    pi32ConfigAttribs[11] = 8;
    pi32ConfigAttribs[12] = EGL_NONE;
    */

    pi32ConfigAttribs[4] = EGL_DEPTH_SIZE;
    pi32ConfigAttribs[5] = 8;
    pi32ConfigAttribs[6] = EGL_NONE;



    EGLint pi32ContextAttribs[3];
    pi32ContextAttribs[0] = EGL_CONTEXT_CLIENT_VERSION;
    pi32ContextAttribs[1] = 2;
    pi32ContextAttribs[2] = EGL_NONE;

    EGLint iConfigs;
    if (!eglChooseConfig(eglDisplay, pi32ConfigAttribs, &eglConfig, 1, &iConfigs)
            || (iConfigs != 1))  {
        qDebug() << "Error: eglChooseConfig() failed.\n";
        CleanupAndExit (eglDisplay);
    }

    qDebug() << "theGameWindow->eglChooseConfig()";


#ifdef Q_OS_SYMBIAN
    EGLNativeWindowType pwd = (void*)(this->winId()->DrawableWindow());
#else
    EGLNativeWindowType pwd = (EGLNativeWindowType)this->winId();
#endif
    eglSurface = eglCreateWindowSurface(eglDisplay, eglConfig, pwd, NULL);
    qDebug() << "theGameWindow->eglCreateWindowSurface()";

    if (!TestEGLError("eglCreateWindowSurface")) {
        CleanupAndExit (eglDisplay);
    }

    eglContext = eglCreateContext(eglDisplay, eglConfig, NULL,
                                  pi32ContextAttribs);
    qDebug() << "theGameWindow->eglCreateContext()";

    if (!TestEGLError("eglCreateContext")) {
        CleanupAndExit(eglDisplay);
    }

    eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext);
    qDebug() << "theGameWindow->eglMakeCurrent()";

    if (!TestEGLError("eglMakeCurrent")) {
        CleanupAndExit (eglDisplay);
    }
}


bool GameWindow::TestEGLError(const char* pszLocation)
{
    EGLint iErr = eglGetError();
    if (iErr != EGL_SUCCESS) {
        qDebug() <<  "%s failed (%d).\n" <<  pszLocation << iErr;
        return false;
    }
    return true;
}


void GameWindow::CleanupAndExit ( EGLDisplay eglDisplay )
{
    eglMakeCurrent(eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
    eglTerminate(eglDisplay);
    exit(0);
}


void GameWindow::render()
{
    m_prevTime = m_currentTime;
    m_currentTime = getTickCount();
    m_frameTime = (float)( m_currentTime - m_prevTime ) * 0.001f;
    if (m_frameTime != 0.0f)
        m_fps = 1.0f / m_frameTime;
    else
        m_fps = 100000.0f;


    update( m_frameTime );

    if (m_audioOutput && m_audioOutput->usingThead()==false)
        m_audioOutput->tick();         // manual tick


    onRender();

    if (!eglSwapBuffers(eglDisplay, eglSurface)) {
        GLint errval = eglGetError();
        /*
        EXPLANATION:
        http://library.forum.nokia.com/index.jsp?topic=/Nokia_Symbian3_Developers_Library/GUID-894AB487-C127-532D-852B-37CB0DEA1440.html
        On the Symbian platform, EGL handles the window resize in the next
        call to eglSwapBuffers(), which resizes the surface to match the new
        window size. If the preserve buffer option is in use, this function
        also copies across all the pixels from the old surface that overlap
        the new surface, although the exact details depend on the
        implementation.If the surface resize fails, eglSwapBuffers() returns
        EGL_FALSE and an EGL_BAD_ALLOC error is raised. This may mean that
        the implementation does not support the resizing of a surface or that
        there is not enough memory available (on a platform with GPU, this
        would be GPU rather than system memory). Applications must always
        monitor whether eglSwapBuffers() fails after a window resize.
        When it does fail, the application should do the following:
        Call eglDestroySurface() to destroy the current EGL window surface.
        Call eglCreateWindowSurface() to recreate the EGL window surface.
        This may cause a noticeable flicker and so should be done only when
        necessary.
       */

        qDebug() << "eglSwapbuffers failed with error: " << errval;
        if (errval == EGL_BAD_ALLOC || errval == EGL_BAD_SURFACE) {
            if (errval == EGL_BAD_ALLOC)
                qDebug() << "Error was bad alloc, .. taking care of it.";

            eglDestroySurface( eglDisplay, eglSurface );

#ifdef Q_OS_SYMBIAN
            EGLNativeWindowType pwd = (void*)(this->winId()->DrawableWindow());
#else
            EGLNativeWindowType pwd = (EGLNativeWindowType)this->winId();
#endif
            eglSurface = eglCreateWindowSurface(eglDisplay,
                                                eglConfig,
                                                pwd,
                                                NULL);

        }
        else
            CleanupAndExit (eglDisplay);
    }
}


void GameWindow::resizeEvent( QResizeEvent *event )
{
    if (event->size().width() == event->oldSize().width()
            && event->size().height() == event->oldSize().height())
        return;

    int w = event->size().width();
    int h = event->size().height();
    qDebug() << "Setting viewport..";
    glViewport(0,0, w, h);
    setSize(w, h);

    qDebug() << "resizeEvent: " << w << "," << h;

    QWidget::resizeEvent( event );
}


void GameWindow::update(const float fFrameDelta)
{
}


void GameWindow::onRender()
{
    qDebug() << "void GE::GameWindow::onRender()";
}


void GameWindow::destroy()
{
    stopAudio();
    onDestroy();
}


void GameWindow::onDestroy()
{
    qDebug() << "void GE::GameWindow::onDestroy()";
}


void GameWindow::fullscreen()
{
    setWindowState(Qt::WindowFullScreen);
}


void GameWindow::timerEvent(QTimerEvent *event)
{
    render();
}


void GameWindow::pause()
{
    stopAudio();
    killTimer(timerid);
    timerid = 0;
    onPause();
}


void GameWindow::resume()
{
    if(timerid)
        return;

#ifdef Q_OS_SYMBIAN
    User::After(500000);
#endif


    if (isProfileSilent() == false) startAudio();

    onResume();
    timerid = startTimer(0);
}
